#!/usr/bin/env python
# -*- coding: utf-8 -*-

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
#  Wireless Interface Module
#
#  Developed by Joao Rodrigues - gothicknight@gmail.com
#
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
#    This file is part of wifiTools.
#
#    wifiTools is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    any later version.
#
#    wifiTools is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with wifiTools; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


# Bibliotecas de sistema
import os
import sys

import array
import fcntl
import socket
import struct

# Outras bibliotecas externas
from subprocess import Popen, PIPE

# User information
uid		= os.getuid()

# Wireless system paths
#wifiFile	= '/proc/net/wireless'

# Sinais de Controlo
SIOCGIWESSID	= 0x8B1B	# Get ESSID
SIOCGIWMODE	= 0x8B07	# Get Mode
SIOCGIWRATE	= 0x8B21	# Get Rate

SIOCGIWNWID	= 0x8B03	# Get network id (the cell)
SIOCGIWFREQ	= 0x8B05	# Get channel/frequency (Hz)
SIOCGIWSENS	= 0x8B09	# Get sensitivity (dBm)

SIOCGIWAP	= 0x8B15	# Get access point MAC addresses
SIOCGIWTXPOW	= 0x8B27	# Get transmit power (dBm)
SIOCGIWENCODE	= 0x8B2B	# Get encoding token & mode

# Constantes da Wireless
wKILO = 10**3
wMEGA = 10**6
wGIGA = 10**9

# Constantes de Byte
bKILO = 2**10
bMEGA = 2**20
bGIGA = 2**30

# Modos
modes = ['Auto', 'Ad-Hoc', 'Managed', 'Master', 'Repeat', 'Second', 'Monitor']

# Socket
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

#-------------------------------------------------------------------------------------------------
def version():
	"""Versao da Libraria"""

	return "v.20070221"
#-------------------------------------------------------------------------------------------------
def getWSettings(dev):
# DEVELOPMENT

	DevState = {}

	DevState["ESSID"]	= returnESSID(dev)
	DevState["BitRate"]	= returnBitrate(dev)
	DevState["Modo"]	= returnMode(dev)
	DevState["Protocolo"]	= "NaoImplementado"
	DevState["AP"]		= "NaoImplementado"
	DevState["Freq"]	= "NaoImplementado"
	
	return DevState
#-------------------------------------------------------------------------------------------------
# TEST
# Patched - Tiago Cogumbreiro <cogumbreiro@users.sf.net>
def getWSet(dev):
	"""iwconfig parser"""
	
	DevState = {}
	
	if uid != 0:
		print >>sys.stderr.write(" W Podes nao ter permissoes para configurar/verificar redes!")
	p = Popen("iwconfig "+str(dev),shell=True, stdout=PIPE)
	out = p.stdout.read().splitlines()
	p.stdout.close()
	# -----------------------------------------
	# Em debian nao e possivel executar o iwconfig como utilizador
	# -----------------------------------------
	if len( out ) == 0:
		print >>sys.stderr.write(" E FATAL: Nao foi possivel determinar o estado do dispositivo"+dev)
		return {}
	# ----------------------------------------- LINHA 1
	temp = out[0].split()
	# Protocolo
	DevState["Protocolo"]=" ".join(temp[1:3])
	# SSID
	DevState["ESSID"] = temp[3].split(":")[1]
	# ----------------------------------------- LINHA 2
	temp = out[1].split("  ")
	# Modo
	DevState["Modo"] = temp[5].split(":")[1]
	# Frequencia
	try:
		DevState["Freq"] = temp[6].split(":")[1]
	except IndexError:
		DevState["Freq"] = temp[6].split("=", 1)[1]

	# AP
	if "Access" in temp[7].split(" ")[0]:
		DevState["AP"] = temp[7].split(" ")[2]
	else:
		DevState["AP"] = temp[7].split(" ")[1]
	# ----------------------------------------- LINHA 3
	temp = out[2].split("  ")

	# Workaround para versoes diferentes diff( ":" e "=" )
	try:
		DevState["BitRate"] = temp[5].split(":")[1]
	except:
		DevState["BitRate"] = temp[5].split("=")[1]

	return DevState
#-------------------------------------------------------------------------------------------------
# DONE
def getWParams(dev):

	DevStatFile="/sys/class/net/"+str(dev)+"/wireless/"
	DevState= {}

	for elem in ["beacon","crypt","fragment","level","link","misc","noise","nwid","retries"]:
		if os.access(DevStatFile+elem, os.F_OK|os.R_OK):

			file_io=open(DevStatFile+elem)
			try:
				DevState[elem] = int(file_io.read())
			except:
				DevState[elem] = str(file_io.read())
			file_io.close()
		else:
			DevState[elem]=-1
	return DevState
#-------------------------------------------------------------------------------------------------
# DONE
def setWNetwork(dev,SSID,key="",mode="Ad-hoc"):
	"""Configura uma rede args:(dev,SSID,key,mode)"""

	if uid != 0:
		print >>sys.stderr.write(" E Nao tens permissoes para criar redes")
		return -1
	# Desactivar o dispositivo
	if SSID == "off":
		if Wext():
			os.system("iwconfig "+str(dev)+" essid off; ifconfig "+str(dev)+" down")
		os.system("ifconfig "+str(dev)+" down")
		return

	pipeStr = "iwconfig "+str(dev)+" essid "+SSID+" mode "+mode
	if key != "":
		pipeStr+=" key "+key

	# Cria a rede
	os.system(pipeStr)
#-------------------------------------------------------------------------------------------------
# DONE
def getWDevs():
	"""Funcao para detectar dispositivos wireless existentes no computador"""

	wifi_devs	= []

	for interface in os.listdir("/sys/class/net"):
		if os.path.exists(os.path.join("/sys/class/net", interface, "wireless")):
			wifi_devs.append(interface)
	print " * Detectado(s) "+str( len(wifi_devs) )+" dispositivo(s) wireless: "+" ".join(wifi_devs)
	return wifi_devs
#-------------------------------------------------------------------------------------------------
# DONE
def getWNet(dev):
	""" Pesquisa por redes wireless ao alcance args:(dev)"""

	nCells		= 0
	WAddr		= []
	WEssid		= []
	WProt		= []
	WMode		= []
	WFreq		= []
	WChan		= []
	WQual		= []
	WSig		= []
	WNoise		= []
	WEnc		= []

	if not(getWext(dev)):
		print >>sys.stderr.write(" E Dispositivo nao suporta Wireless Extensions")
		return [-1]

	p = Popen("iwlist "+str(dev)+" scan",shell=True, stdout=PIPE)
	out = p.stdout.readlines()
	p.stdout.close()
	nCells = len([ elem for elem in out if "Cell 0" in elem ])
	print " * Detectada(s) %d rede(s)" % nCells
	if nCells == 0:
		return [0]
	for elem in out:
		if "Address:" in elem and not ( "ESSID:" in elem ):
			WAddr.append(str(elem.split(" ")[14]).split("\n")[0])
		if "ESSID:" in elem:
			#print str(elem.split(":")[1]).split("\n")[0].split("\"")[1]
			WEssid.append(str(elem.split(":")[1]).split("\n")[0])
		if "Protocol:" in elem and not ( "ESSID:" in elem ):
			WProt.append(str(elem.split(":")[1]).split("\n")[0])
		if "Mode:" in elem and not ( "ESSID:" in elem ):
			WMode.append(str(elem.split(":")[1]).split("\n")[0])
		if "Frequency:" in elem and not ( "ESSID:" in elem ):
			WFreq.append(str(elem.split(":")[1]).split(" ")[0])
			WChan.append(str(str(elem.split(":")[1]).split(" ")[3]).split(")")[0])
		if "Quality:" in elem and not ( "ESSID:" in elem ):
			WQual.append(str(elem.split(":")[1]).split(" ")[0])
			WSig.append(str(elem.split(":")[2]).split(" ")[0])
			WNoise.append(str(elem.split(":")[3]).split(" ")[0])
		if "Encryption key:" in elem and not ( "ESSID:" in elem ):
			WEnc.append(str(elem.split(":")[1]).split("\n")[0])
	print "-------------------------------------------------------"	
	print "\t> AP: "+"; ".join(WAddr)
	print "\t> ESSID: "+"; ".join(WEssid)
	print "\t> Protocolo: "+"; ".join(WProt)
	print "\t> Modo: "+"; ".join(WMode)
	print "\t> Frequencia: "+"; ".join(WFreq)
	print "\t> Canal: "+"; ".join(WChan)
	print "\t> Qualidade: "+"; ".join(WQual)
	print "\t> Sinal: "+"; ".join(WSig)
	print "\t> Ruido: "+"; ".join(WNoise)
	print "\t> Encriptada: "+"; ".join(WEnc)
	print "-------------------------------------------------------"
	return [nCells, WAddr, WEssid, WProt, WMode, WFreq, WChan, WQual, WSig, WNoise, WEnc ]

#-------------------------------------------------------------------------------------------------
def getWext(dev):
	if not(os.path.isdir("/sys/class/net/"+str(dev)+"/wireless")):
		print >>sys.stderr.write("\tE Este dispositivo nao tem extencoes wireless")
		return False
	else:
		return True
#-------------------------------------------------------------------------------------------------
# Daqui para baixo, codigo retirado do PyWireless.py
#-------------------------------------------------------------------------------------------------
def returnESSID(dev):
	''' Returns essid of interface '''
	buffer, structure = __packRequest(32)
	i, result = __readInformation(dev,SIOCGIWESSID, structure)
	if i > 0:
		return result
	return buffer.tostring().strip('\x00')
#-------------------------------------------------------------------------------------------------
def returnBitrate( dev ):
	i, result = __readInformation(dev, SIOCGIWRATE)
	if i > 0:
		return result

	size = struct.calcsize('ihbb')
	m, e, i, pad = struct.unpack('ihbb', result[:size])
	if e == 0:
		bitrate =  m
	else:
		bitrate = float(m) * 10**e

	if bitrate >= wGIGA:
		return "%i Gb/s" %(bitrate/wGIGA)

	if bitrate >= wMEGA:
		return "%i Mb/s" %(bitrate/wMEGA)

	if bitrate >= wKILO:
		return "%i Kb/s" %(bitrate/wKILO)
#-------------------------------------------------------------------------------------------------
def returnMode(dev):
	i, result = __readInformation(dev, SIOCGIWMODE)
	if i > 0:
		return result
	mode = __unpackRequest('i', result[:4])[0]
	return modes[mode]
#-------------------------------------------------------------------------------------------------
def returnAP(dev):
	""" NAO IMPLEMENTADO"""
	i, result = __readInformation(dev, SIOCGIWAP)
	if i > 0:
		return result
#	print "--------->"+str(result)+"<-------"
#	print __unpackRequest('II', result)
#-------------------------------------------------------------------------------------------------
def returnFreq(dev):
	""" NAO IMPLEMENTADO"""
	i, result = __readInformation(dev, SIOCGIWFREQ)
	if i > 0:
		return result
	print "--------->"+str(result)+"<-------"
	print __unpackRequest('II', result[:])
#-------------------------------------------------------------------------------------------------
# Metodos Internos da libraria
#-------------------------------------------------------------------------------------------------
def __packRequest(bufferSize):
	""" Packs wireless request data for sending it to the kernel """
	buffer = array.array('c', '\0' * bufferSize)
	caddr_t, length = buffer.buffer_info()
	structure = struct.pack('Pi', caddr_t, length)
	return buffer, structure
#-------------------------------------------------------------------------------------------------
def __unpackRequest(format, packedRequest):
	""" Unpacks request with given format """
	return struct.unpack(format, packedRequest)
#-------------------------------------------------------------------------------------------------
def __readInformation(dev, request, data = None):
	''' Read information from interface '''
	if data is not None:
		buffer = 16 - len(dev)
		requestedInterface = dev + '\0' * buffer
		requestedInterface += data
	else:
		requestedInterface = (dev + '\0' * 32)
	try:
		result = fcntl.ioctl( sockfd.fileno(), request, requestedInterface)
	except IOError, (i, e):
		return i, e

	return (0, result[16:])
#-------------------------------------------------------------------------------------------------
